﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Coleotrack.Controllers;
using Coleotrack.Core.Helper;
using Coleotrack.Data;
using Coleotrack.Models;
using Coleotrack.Service.Contracts;
using Coleotrack.Web;
using Moq;
using NUnit.Framework;
using Coleotrack;

namespace Coleotrack.Test.Controllers
{
    [TestFixture]
    public class AccountControllerTest : ConcernOf<AccountController>
    {
        Mock<IFormsAuthentication> mockFormsAuth;
        Mock<IMembershipService> mockMembership;
        Mock<IUserService> mockUserService;

        [SetUp]
        public override void Setup()
        {
            mockFormsAuth = new Mock<IFormsAuthentication>();
            mockMembership = new Mock<IMembershipService>();
            mockUserService = new Mock<IUserService>();

            mockMembership.Setup(m => m.MinPasswordLength).Returns(6);

            Sut = new AccountController(mockFormsAuth.Object, mockMembership.Object, mockUserService.Object);
            Sut.SetFakeControllerContext(false);
        }

        [Test, Category("Common")]
        public void Properties_Are_Set()
        {
            Assert.NotNull(Sut.FormsAuth);
            Assert.NotNull(Sut.MembershipService);
        }

        [Test, Category("LogOn")]
        public void LogOn_Get_Should_Return_View()
        {
            ActionResult result = Sut.LogOn();

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("LogOn")]
        public void Invalid_LogOn_Should_Return_View()
        {
            mockMembership.Setup(m => m.ValidateUser("valid-user", "password")).Returns(true);

            ActionResult result = Sut.LogOn(new LogOnViewModel()
                                                {
                                                    Password = "wrong-password",
                                                    ReturnUrl = "/",
                                                    Username = "wrong-user"
                                                });

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("LogOn")]
        public void Valid_LogOn_Should_Redirect_To_ReturnUrl_When_ReturnUrl_Is_Given()
        {
            mockMembership.Setup(m => m.ValidateUser("valid-user", "password".Md5())).Returns(true);

            var model = new LogOnViewModel()
                            {
                                Password = "password",
                                ReturnUrl = "/",
                                Username = "valid-user"
                            };
            ActionResult result = Sut.LogOn(model);

            Assert.IsInstanceOf(typeof(RedirectResult), result);
            Assert.AreEqual(model.ReturnUrl, ((RedirectResult)result).Url);
        }

        [Test, Category("LogOn")]
        public void Valid_LogOn_Should_Redirect_To_Home_When_ReturnUrl_Is_Not_Given()
        {
            mockMembership.Setup(m => m.ValidateUser("valid-user", "password".Md5())).Returns(true);

            var model = new LogOnViewModel()
            {
                Password = "password",
                Username = "valid-user"
            };
            ActionResult result = Sut.LogOn(model);

            Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
            Assert.AreEqual("Index", ((RedirectToRouteResult)result).RouteValues["Action"]);
            Assert.AreEqual("Home", ((RedirectToRouteResult)result).RouteValues["Controller"]);
        }

        [Test, Category("LogOff")]
        public void LogOff()
        {
            ActionResult result = Sut.LogOff();

            Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
            Assert.AreEqual("Index", ((RedirectToRouteResult)result).RouteValues["Action"]);
            Assert.AreEqual("Home", ((RedirectToRouteResult)result).RouteValues["Controller"]);
        }

        #region Registration

        [Test, Category("Registration")]
        public void Registration_Get_Should_Return_Register_View()
        {
            var result = Sut.Register();
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Valid_Data_Should_Return_AccountCreated_View()
        {
            User newUser = new User();
            newUser.Name = "UserXYZ";
            newUser.Password = "123456";
            newUser.Active = false;

            mockUserService.Setup(u => u.GetByName(newUser.Name)).Returns(newUser);

            var result = Sut.Register(
                new RegisterViewModel
                {
                    Username = "UserXYZ",
                    Email = "me@example.com",
                    EmailConfirmation = "me@example.com",
                    Password = "123456",
                    PasswordConfirmation = "123456"
                });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("AccountCreated", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Invalid_Email_Address_Should_Return_Register_View()
        {
            var result = Sut.Register(
                new RegisterViewModel
                    {
                        Username = "test",
                        Email = "@missing.com",
                        EmailConfirmation = "@missing.com",
                        Password = "123456",
                        PasswordConfirmation = "123456"
                    });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Unequal_Email_Addresses_Should_Return_Register_View()
        {
            var result = Sut.Register(
                new RegisterViewModel
                {
                    Username = "test",
                    Email = "me@example.com",
                    EmailConfirmation = "you@example.com",
                    Password = "123456",
                    PasswordConfirmation = "123456"
                });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Empty_Password_Should_Return_Register_View()
        {
            var result = Sut.Register(
                new RegisterViewModel
                {
                    Username = "test",
                    Email = "me@example.com",
                    EmailConfirmation = "me@example.com",
                    Password = "",
                    PasswordConfirmation = ""
                });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Unequal_Passwords_Should_Return_Register_View()
        {
            var result = Sut.Register(
                new RegisterViewModel
                {
                    Username = "test",
                    Email = "me@example.com",
                    EmailConfirmation = "me@example.com",
                    Password = "123456",
                    PasswordConfirmation = "654321"
                });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Empty_Username_Should_Return_Register_View()
        {
            var result = Sut.Register(
                new RegisterViewModel
                {
                    Email = "me@example.com",
                    EmailConfirmation = "me@example.com",
                    Password = "123456",
                    PasswordConfirmation = "123456"
                });
            Assert.IsInstanceOf(typeof(ViewResult), result);
            Assert.AreEqual("Register", ((ViewResult)result).ViewName);
        }

        [Test, Category("Registration")]
        public void Registration_Existing_Username_Should_Return_Register_View()
        {
            //throw new NotImplementedException("Registration_Existing_Username_Should_Return_View");
        }

        [Test, Category("Registration")]
        public void Registration_Existing_Email_Address_Should_Return_Register_View()
        {
            //throw new NotImplementedException("Registration_Existing_Email_Address_Should_Return_View");
        }

        #endregion

        [Test, Category("Profile")]
        public void Profile_Get_Should_Return_View()
        {
            ActionResult result = Sut.Profile();
            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_Get_Should_Return_View()
        {
            // Arrange

            // Act
            ActionResult result = Sut.ChangePassword();

            // Assert
            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_Actual_Password_Is_Wrong_Should_Return_View()
        {
            // Arrange
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "wrongpassword";
            model.NewPassword = "newPassword";
            model.NewPasswordConfirmation = "newPassword";

            // Act
            ActionResult result = Sut.ChangePassword(model);

            // Assert
            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_Password_Confirmation_Not_Equal_Return_View()
        {
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = "newpassword";
            model.NewPasswordConfirmation = "otherPassword";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof (ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_NewPassword_Is_Empty_Should_Return_View()
        {
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = string.Empty;
            model.NewPasswordConfirmation = "otherPassword";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_NewPassword_Is_Null_Should_Return_View()
        {
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = null;
            model.NewPasswordConfirmation = "otherPassword";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_NewPassword_Contains_Only_Whitespaces_Should_Return_View()
        {
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = "      ";
            model.NewPasswordConfirmation = "otherPassword";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_NewPassword_Is_Shorter_Than_Six_Chars_Should_Return_View()
        {
            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = "short";
            model.NewPasswordConfirmation = "short";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof(ViewResult), result);
        }

        [Test, Category("ChangePassword")]
        public void ChangePassword_All_Passwords_Ok_Should_Redirect_To_Home()
        {
            mockMembership.Setup(m => m.ValidateUser("valid-user", "password".Md5())).Returns(true);

            ChangePasswordViewModel model = new ChangePasswordViewModel();
            model.Password = "password";
            model.NewPassword = "newpassword";
            model.NewPasswordConfirmation = "newpassword";

            ActionResult result = Sut.ChangePassword(model);

            Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
            Assert.AreEqual("Index", ((RedirectToRouteResult)result).RouteValues["Action"]);
            Assert.AreEqual("Home", ((RedirectToRouteResult)result).RouteValues["Controller"]);
        }

        [TearDown]
        public override void Teardown()
        {
            // nothing to do
        }
    }
}